package com.jhh.pay.center.service.cloud;

import com.alibaba.fastjson.JSON;
import com.google.common.base.Strings;
import com.jhh.pay.center.model.OrderType;
import com.jhh.pay.center.model.PayChannel;
import com.jhh.pay.center.model.TradeState;
import com.jhh.pay.center.service.cloud.message.*;
import com.jhh.pay.center.service.cloud.path.PayChannelApiPathGetter;
import com.jhh.pay.center.service.cloud.route.RibbonAlgorithm;
import com.jhh.pay.center.service.cloud.route.RouterAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author tangxd
 * @Description: TODO
 * @date 2017/12/8
 */
@Service
@Slf4j
public class DefaultPayChannelTemplate implements PayChannelTemplate {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private CloudServiceDiscovery discovery;

    @Autowired
    private PayChannelApiPathGetter pathGetter;

    private final static Map<String, RouterAlgorithm> ROUTERS = new ConcurrentHashMap<>();

    private String getServiceAddress(String serviceName) {
        RouterAlgorithm router;
        if (!ROUTERS.containsKey(serviceName)) {
            router = new RibbonAlgorithm(discovery, serviceName);
            ROUTERS.put(serviceName, router);
        } else {
            router = ROUTERS.get(serviceName);
        }
        CloudServiceInstance instance = router.get();
        if (null == instance) {
            return null;
        }
        return String.format("%s://%s", instance.getScheme(), instance.getHostPort());
    }

    private String replaceVersion(String urlPath, String version) {
        return urlPath.replace("{version}", version);
    }

    private TradeResult noServiceResult(PayChannel channel, String sid) {
        TradeResult result = new TradeResult(TradeState.ERROR);
        result.setSid(sid);
        result.setChannelId(channel.getId());
        result.setCode("SYS:0001");
        result.setMsg(channel.getName() + "没有可用的支付服务节点在线");
        return result;
    }

    private TradeResult reqFailResult(PayChannel channel, String sid) {
        TradeResult result = new TradeResult(TradeState.ERROR);
        result.setSid(sid);
        result.setChannelId(channel.getId());
        result.setCode("SYS:0002");
        result.setMsg(channel.getName() + "调用支付服务节点异常");
        return result;
    }

    private TradeResult post(String url, AbstractMessage data, PayChannel channel) {
        try {
            TradeResult resp = restTemplate.postForObject(url, data, TradeResult.class);
            if(Strings.isNullOrEmpty(resp.getSid())) {
                resp.setSid(data.getSid());
            }
            resp.setChannelId(channel.getId());
            return resp;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return reqFailResult(channel, data.getSid());
        }
    }

    @Override
    public TradeResult pay(PayChannel channel, TradePayBody body) {
        String reqUrl = getServiceAddress(channel.getServiceName());
        String urlPath = pathGetter.getPath("pay");
        if (reqUrl == null) {
            return noServiceResult(channel, body.getSid());
        }
        if (Strings.isNullOrEmpty(urlPath)) {
            throw new IllegalArgumentException("URL PATH的pay参数为配置");
        }
        String url = reqUrl + replaceVersion(urlPath, channel.getVersion());
        return post(url, body, channel);
    }

    @Override
    public TradeResult deduct(PayChannel channel, TradeDeductBody body) {
        String reqUrl = getServiceAddress(channel.getServiceName());
        String urlPath = pathGetter.getPath("deduct");
        if (reqUrl == null) {
            return noServiceResult(channel, body.getSid());
        }
        if (Strings.isNullOrEmpty(urlPath)) {
            throw new IllegalArgumentException("URL PATH的deduct参数为配置");
        }
        String url = reqUrl + replaceVersion(urlPath, channel.getVersion());
        return post(url, body, channel);
    }

    @Override
    public TradeResult state(PayChannel channel, TradeStateBody body, OrderType type) {
        String reqUrl = getServiceAddress(channel.getServiceName());
        String urlPath = pathGetter.getPath("state");
        if (reqUrl == null) {
            return noServiceResult(channel, body.getSid());
        }
        if (Strings.isNullOrEmpty(urlPath)) {
            throw new IllegalArgumentException("URL PATH的state参数为配置");
        }
        String path = replaceVersion(urlPath, channel.getVersion());
        if (type == OrderType.PAY) {
            path = path.replace("{action}", "pay");
        } else if (type == OrderType.DEDUCT) {
            path = path.replace("{action}", "deduct");
        }
        String url = reqUrl + path;
        return post(url, body, channel);
    }

    @Override
    public TradeResult callbackResolve(PayChannel channel, NotifyResolveBody body) {
        String reqUrl = getServiceAddress(channel.getServiceName());
        String urlPath = pathGetter.getPath("resolve");
        if (reqUrl == null) {
            return noServiceResult(channel, null);
        }
        if (Strings.isNullOrEmpty(urlPath)) {
            throw new IllegalArgumentException("URL PATH的resolve参数为配置");
        }
        String path = replaceVersion(urlPath, channel.getVersion());
        String url = reqUrl + path;

        return post(url, body, channel);
    }

    @Override
    public Map<String, Object> cfg(PayChannel channel, String version) {
        String reqUrl = getServiceAddress(channel.getServiceName());
        String urlPath = pathGetter.getPath("cfg");
        if (reqUrl == null) {
            return null;
        }
        if (Strings.isNullOrEmpty(urlPath)) {
            throw new IllegalArgumentException("URL PATH的cfg参数为配置");
        }
        String path = replaceVersion(urlPath, version);
        String url = reqUrl + path;
        try {
            String resp = restTemplate.getForObject(url, String.class);
            return JSON.parseObject(resp);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }
}
